<?php

namespace Zqbx\Helper;

class GeolocationHelper
{

    /**
     * 计算2个经纬度之间的距离
     * @param $longitude1
     * @param $latitude1
     * @param $longitude2
     * @param $latitude2
     * @param $unit 单位 1m 2km
     * @param $decimal 精度
     * @return float
     */
    public static function getDistances($longitude1, $latitude1, $longitude2, $latitude2, $unit = 1, $decimal = 2)
    {
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI = 3.1415926;

        $radLat1 = $latitude1 * $PI / 180.0;
        $radLat2 = $latitude2 * $PI / 180.0;

        $radLng1 = $longitude1 * $PI / 180.0;
        $radLng2 = $longitude2 * $PI / 180.0;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS * 1000;

        if ($unit == 2) {
            $distance = $distance / 1000;
        }

        return round($distance, $decimal);
    }

    /**
     * 判断一个坐标点是否在多边形内部
     *  可以使用“射线法”或“奇偶交规则”。以下是使用射线法的实现
     *
     * @param $point 坐标点        [112.553571,37.815559]
     * @param $polygon  多边形数组
     *  如下为高德返回的，太原小店文华苑社区
     *  [[112.5547, 37.816182], [112.557029, 37.816282], [112.55704, 37.817214], [112.561683, 37.817223], [112.561718, 37.813864], [112.557576, 37.814208], [112.553492, 37.814666], [112.552868, 37.815048], [112.553514, 37.81505], [112.553505, 37.815695], [112.554693, 37.815707]];
     * @return bool
     */
    public static function isPointInsidePolygon($point, $polygon)
    {
        $x = $point[0];
        $y = $point[1];

        if (!is_array($polygon)) {
            return false;
        }

        $intersections = 0;
        $polygon_count = count($polygon);

        for ($i = 0, $j = $polygon_count - 1; $i < $polygon_count; $j = $i++) {
            $xi = $polygon[$i][0];
            $yi = $polygon[$i][1];
            $xj = $polygon[$j][0];
            $yj = $polygon[$j][1];

            $intersect = ($yi > $y) != ($yj > $y)
                && ($x < ($xj - $xi) * ($y - $yi) / ($yj - $yi) + $xi);

            if ($intersect) {
                $intersections++;
            }
        }

        return $intersections % 2 == 1;
    }

    /**
     * 计算顺路率，不管是否逆向，只计算点位重合
     * @param $trajectory1
     * @param $trajectory2
     * @param $threshold
     * @return float|int
     * @author hgq <393210556@qq.com>.
     * @date: 2024/09/06 18:31
     */
    public static function computeAlongRouteRate($trajectory1, $trajectory2, $threshold = 10) {
        ini_set('memory_limit', '-1');

        $overlapping_points = 0;
        $total_points = count($trajectory1);

        if ($total_points == 0) {
            return 0;
        }

        foreach ($trajectory1 as $point1) {
            foreach ($trajectory2 as $point2) {
                if (self::getDistances($point1[0], $point1[1], $point2[0], $point2[1]) <= $threshold) {
                    $overlapping_points++;
                    break; // 找到一个重合点即可，不需要继续查找
                }
            }
        }

        return round($overlapping_points / $total_points * 100, 2);
    }

    /**
     * 获取轨迹中某个点的方向向量
     * @param array $trajectory 轨迹，格式为 [[lat, lon], [lat, lon], ...]
     * @param int $index 当前点的索引
     * @return array 方向向量，格式为 [dx, dy]
     */
    public static function getDirectionVector($trajectory, $index) {
        // 如果是最后一个点，无法计算方向，返回零向量
        if ($index >= count($trajectory) - 1) {
            return [0, 0];
        }

        $lat1 = $trajectory[$index][0];
        $lon1 = $trajectory[$index][1];
        $lat2 = $trajectory[$index + 1][0];
        $lon2 = $trajectory[$index + 1][1];

        // 计算方向向量，注意这里不需要计算实际距离，只需要相对变化
        $dx = $lat2 - $lat1;
        $dy = $lon2 - $lon1;

        return [$dx, $dy];
    }

    /**
     * 计算两个方向向量之间的夹角（单位：度）
     * @param array $vectorA 方向向量A，格式为 [dx, dy]
     * @param array $vectorB 方向向量B，格式为 [dx, dy]
     * @return float 夹角，单位为度
     */
    public static function getAngleBetweenVectors($vectorA, $vectorB) {
        $dotProduct = $vectorA[0] * $vectorB[0] + $vectorA[1] * $vectorB[1];  // 向量点积
        $magnitudeA = sqrt($vectorA[0] * $vectorA[0] + $vectorA[1] * $vectorA[1]);  // 向量A的模
        $magnitudeB = sqrt($vectorB[0] * $vectorB[0] + $vectorB[1] * $vectorB[1]);  // 向量B的模

        if ($magnitudeA == 0 || $magnitudeB == 0) {
            return 180;  // 如果有一个向量是零向量，认为角度差为180度
        }

        // 计算夹角的余弦值
        $cosTheta = $dotProduct / ($magnitudeA * $magnitudeB);

        // 防止浮点数误差导致cosTheta超出[-1, 1]范围
        $cosTheta = max(-1, min(1, $cosTheta));

        // 计算角度并转换为度数
        $theta = rad2deg(acos($cosTheta));

        return $theta;
    }

    /**
     * 计算一条轨迹相对于另一条轨迹的顺路率
     * @param array $trajectoryA 基准轨迹，格式为 [[lat, lon], [lat, lon], ...]
     * @param array $trajectoryB 被比较的轨迹，格式为 [[lat, lon], [lat, lon], ...]
     * $type 1 可以捎到需要捎 2 需要捎到可以捎
     * @param float $distance_threshold 最大允许的距离，单位为米
     * @param float $angle_threshold 最大允许的方向角度差，单位为度
     * @return float 顺路率，范围 [0, 100]
     */
    public static function computeOrderedAlongRouteRate($trajectoryA, $trajectoryB, $type = 1, $distance_threshold = 50, $angle_threshold = 30) {
        ini_set('memory_limit', '-1');
        $overlapping_points = 0;
        if ($type == 1) {
            $total_points = count($trajectoryB);
            $cale_total_points = count($trajectoryA);
        } else {
            $total_points = count($trajectoryA);
            $cale_total_points = $total_points;
        }

        if ($total_points == 0) {
            return 0;  // 如果基准轨迹没有点，无法计算顺路率
        }

        // 遍历基准轨迹中的每个点
        for ($i = 0; $i < $cale_total_points; $i++) {
            $latA = $trajectoryA[$i][0];
            $lonA = $trajectoryA[$i][1];

            // 标志是否找到符合条件的点
            $found_match = false;

            // 在被比较轨迹中寻找与基准轨迹点符合条件的点
            foreach ($trajectoryB as $pointB) {
                $latB = $pointB[0];
                $lonB = $pointB[1];

                // 计算距离
                $distance = self::getDistances($latA, $lonA, $latB, $lonB);
                if ($distance > $distance_threshold) {
                    continue;  // 距离超过阈值，跳过
                }

                // 计算方向差
                $vectorA = self::getDirectionVector($trajectoryA, $i);
                $vectorB = self::getDirectionVector($trajectoryB, array_search($pointB, $trajectoryB));
                $angle_diff = self::getAngleBetweenVectors($vectorA, $vectorB);
                if ($angle_diff > $angle_threshold) {
                    continue;  // 角度差超过阈值，跳过
                }

                // 找到符合条件的点
                $found_match = true;
                break;  // 不需要找最小距离，只要找到一个符合条件的点就可以了
            }

            // 如果找到了符合条件的点，认为这个点是顺路的
            if ($found_match) {
                $overlapping_points++;
            }
        }

        // 计算顺路率
        return round($overlapping_points / $total_points * 100, 2);
    }
}